home *** CD-ROM | disk | FTP | other *** search
- /* xfprintf.c -
- X/Open extended v?fprintf implemented in terms of v?fprintf.
-
- Written by James Clark (jjc@jclark.com).
- */
-
- /* Compile with:
-
- -DVARARGS to use varargs.h instead of stdarg.h
- -DLONG_DOUBLE_MISSING if your compiler doesn't like `long double'
- -DFP_SUPPORT to include floating point stuff
- */
-
- #include "config.h"
-
- #ifndef HAVE_EXTENDED_PRINTF
-
- #include "std.h"
-
- #ifdef lint
- /* avoid stupid lint warnings */
- #undef va_arg
- #define va_arg(ap, type) (ap, (type)0)
- #endif
-
- #ifdef FP_SUPPORT
- #ifdef LONG_DOUBLE_MISSING
- typedef double long_double;
- #else
- typedef long double long_double;
- #endif
- #endif /* FP_SUPPORT */
-
- #ifndef __STDC__
- #define const /* as nothing */
- #endif
-
- #ifdef USE_PROTOTYPES
- #define P(parms) parms
- #else
- #define P(parms) ()
- #endif
-
- #ifdef VARARGS
- typedef int (*printer)();
- #else
- typedef int (*printer)(UNIV, const char *, ...);
- #endif
-
- enum arg_type {
- NONE,
- INT,
- UNSIGNED,
- LONG,
- UNSIGNED_LONG,
- #ifdef FP_SUPPORT
- DOUBLE,
- LONG_DOUBLE,
- #endif /* FP_SUPPORT */
- PCHAR,
- PINT,
- PLONG,
- PSHORT
- };
-
- union arg {
- int i;
- unsigned u;
- long l;
- unsigned long ul;
- #ifdef FP_SUPPORT
- double d;
- long_double ld;
- #endif /* FP_SUPPORT */
- char *pc;
- UNIV pv;
- int *pi;
- short *ps;
- long *pl;
- };
-
- #define NEXT 0
- #define MISSING 10
-
- struct spec {
- enum arg_type type;
- char pos;
- char field_width;
- char precision;
- };
-
- #define FLAG_CHARS "-+ #0"
-
- static int parse_spec P((const char **, struct spec *));
- static int find_arg_types P((const char *, enum arg_type *));
- static void get_arg P((enum arg_type, va_list *, union arg *));
- static int do_arg P((UNIV, printer, const char *, enum arg_type, union arg *));
- static int xdoprt P((UNIV, printer, const char *, va_list));
- static int printit P((UNIV, printer, const char *, va_list, int, union arg *));
- static int maybe_positional P((const char *));
-
- /* Return 1 if sucessful, 0 otherwise. **pp points to character after % */
-
- static int parse_spec(pp, sp)
- const char **pp;
- struct spec *sp;
- {
- char modifier = 0;
- sp->pos = NEXT;
- if (isdigit((unsigned char)(**pp)) && (*pp)[1] == '$') {
- if (**pp == '0')
- return 0;
- sp->pos = **pp - '0';
- *pp += 2;
- }
-
- while (**pp != '\0' && strchr(FLAG_CHARS, **pp))
- *pp += 1;
-
- /* handle the field width */
-
- sp->field_width = MISSING;
- if (**pp == '*') {
- *pp += 1;
- if (isdigit((unsigned char)**pp) && (*pp)[1] == '$') {
- if (**pp == '0')
- return 0;
- sp->field_width = **pp - '0';
- *pp += 2;
- }
- else
- sp->field_width = NEXT;
- }
- else {
- while (isdigit((unsigned char)**pp))
- *pp += 1;
- }
-
- /* handle the precision */
- sp->precision = MISSING;
- if (**pp == '.') {
- *pp += 1;
- if (**pp == '*') {
- *pp += 1;
- if (isdigit((unsigned char)**pp) && (*pp)[1] == '$') {
- if (**pp == '0')
- return 0;
- sp->precision = **pp - '0';
- *pp += 2;
- }
- else
- sp->precision = NEXT;
- }
- else {
- while (isdigit((unsigned char)**pp))
- *pp += 1;
- }
- }
- /* handle h l or L */
-
- if (**pp == 'h' || **pp == 'l' || **pp == 'L') {
- modifier = **pp;
- *pp += 1;
- }
-
- switch (**pp) {
- case 'd':
- case 'i':
- sp->type = modifier == 'l' ? LONG : INT;
- break;
- case 'o':
- case 'u':
- case 'x':
- case 'X':
- sp->type = modifier == 'l' ? UNSIGNED_LONG : UNSIGNED;
- break;
- #ifdef FP_SUPPORT
- case 'e':
- case 'E':
- case 'f':
- case 'g':
- case 'G':
- sp->type = modifier == 'L' ? LONG_DOUBLE : DOUBLE;
- break;
- #endif /* FP_SUPPORT */
- case 'c':
- sp->type = INT;
- break;
- case 's':
- sp->type = PCHAR;
- break;
- case 'p':
- /* a pointer to void has the same representation as a pointer to char */
- sp->type = PCHAR;
- break;
- case 'n':
- if (modifier == 'h')
- sp->type = PSHORT;
- else if (modifier == 'l')
- sp->type = PLONG;
- else
- sp->type = PINT;
- break;
- case '%':
- sp->type = NONE;
- break;
- default:
- return 0;
- }
- *pp += 1;
- return 1;
- }
-
-
- static int find_arg_types(format, arg_type)
- const char *format;
- enum arg_type *arg_type;
- {
- int i, pos;
- const char *p;
- struct spec spec;
-
- for (i = 0; i < 9; i++)
- arg_type[i] = NONE;
-
- pos = 0;
-
- p = format;
- while (*p)
- if (*p == '%') {
- p++;
- if (!parse_spec(&p, &spec))
- return 0;
- if (spec.type != NONE) {
- int n;
- if (spec.pos == NEXT)
- n = pos++;
- else
- n = spec.pos - 1;
- if (n < 9) {
- enum arg_type t = arg_type[n];
- if (t != NONE && t != spec.type)
- return 0;
- arg_type[n] = spec.type;
- }
- }
- if (spec.field_width != MISSING) {
- int n;
- if (spec.field_width == NEXT)
- n = pos++;
- else
- n = spec.field_width - 1;
- if (n < 9) {
- enum arg_type t = arg_type[n];
- if (t != NONE && t != INT)
- return 0;
- arg_type[n] = INT;
- }
- }
- if (spec.precision != MISSING) {
- int n;
- if (spec.precision == NEXT)
- n = pos++;
- else
- n = spec.precision - 1;
- if (n < 9) {
- enum arg_type t = arg_type[n];
- if (t != NONE && t != INT)
- return 0;
- arg_type[n] = INT;
- }
- }
- }
- else
- p++;
- return 1;
- }
-
- static void get_arg(arg_type, app, argp)
- enum arg_type arg_type;
- va_list *app;
- union arg *argp;
- {
- switch (arg_type) {
- case NONE:
- break;
- case INT:
- argp->i = va_arg(*app, int);
- break;
- case UNSIGNED:
- argp->u = va_arg(*app, unsigned);
- break;
- case LONG:
- argp->l = va_arg(*app, long);
- break;
- case UNSIGNED_LONG:
- argp->ul = va_arg(*app, unsigned long);
- break;
- #ifdef FP_SUPPORT
- case DOUBLE:
- argp->d = va_arg(*app, double);
- break;
- case LONG_DOUBLE:
- argp->ld = va_arg(*app, long_double);
- break;
- #endif /* FP_SUPPORT */
- case PCHAR:
- argp->pc = va_arg(*app, char *);
- break;
- case PINT:
- argp->pi = va_arg(*app, int *);
- break;
- case PSHORT:
- argp->ps = va_arg(*app, short *);
- break;
- case PLONG:
- argp->pl = va_arg(*app, long *);
- break;
- default:
- abort();
- }
- }
-
- static int do_arg(handle, func, buf, arg_type, argp)
- UNIV handle;
- printer func;
- const char *buf;
- enum arg_type arg_type;
- union arg *argp;
- {
- switch (arg_type) {
- case NONE:
- return (*func)(handle, buf);
- case INT:
- return (*func)(handle, buf, argp->i);
- case UNSIGNED:
- return (*func)(handle, buf, argp->u);
- case LONG:
- return (*func)(handle, buf, argp->l);
- case UNSIGNED_LONG:
- return (*func)(handle, buf, argp->ul);
- #ifdef FP_SUPPORT
- case DOUBLE:
- return (*func)(handle, buf, argp->d);
- case LONG_DOUBLE:
- return (*func)(handle, buf, argp->ld);
- #endif /* FP_SUPPORT */
- case PCHAR:
- return (*func)(handle, buf, argp->pc);
- case PINT:
- return (*func)(handle, buf, argp->pi);
- case PSHORT:
- return (*func)(handle, buf, argp->ps);
- case PLONG:
- return (*func)(handle, buf, argp->pl);
- default:
- abort();
- }
- /* NOTREACHED */
- }
-
- static int printit(handle, func, p, ap, nargs, arg)
- UNIV handle;
- printer func;
- const char *p;
- va_list ap;
- int nargs;
- union arg *arg;
- {
- char buf[512]; /* enough for a spec */
- int count = 0;
- int pos = 0;
-
- while (*p)
- if (*p == '%') {
- char *q;
- struct spec spec;
- const char *start;
- int had_field_width;
- union arg *argp;
- union arg a;
- int res;
-
- start = ++p;
- if (!parse_spec(&p, &spec))
- abort(); /* should have caught it in find_arg_types */
-
- buf[0] = '%';
- q = buf + 1;
-
- if (spec.pos != NEXT)
- start += 2;
-
- /* substitute in precision and field width if necessary */
- had_field_width = 0;
- while (start < p) {
- if (*start == '*') {
- char c;
- int n, val;
-
- start++;
- if (!had_field_width && spec.field_width != MISSING) {
- c = spec.field_width;
- had_field_width = 1;
- }
- else
- c = spec.precision;
- if (c == NEXT)
- n = pos++;
- else {
- start += 2;
- n = c - 1;
- }
- if (n >= nargs)
- val = va_arg(ap, int);
- else
- val = arg[n].i;
-
- /* ignore negative precision */
- if (val >= 0 || q[-1] != '.') {
- (void)sprintf(q, "%d", val);
- q = strchr(q, '\0');
- }
- }
- else
- *q++ = *start++;
- }
- *q++ = '\0';
-
- argp = 0;
- if (spec.type != NONE) {
- int n = spec.pos == NEXT ? pos++ : spec.pos - 1;
- if (n >= nargs) {
- get_arg(spec.type, &ap, &a);
- argp = &a;
- }
- else
- argp = arg + n;
- }
-
- res = do_arg(handle, func, buf, spec.type, argp);
- if (res < 0)
- return -1;
- count += res;
- }
- else {
- if ((*func)(handle, "%c", *p++) < 0)
- return -1;
- count++;
- }
- return count;
- }
-
- /* Do a quick check to see if it may contains any positional thingies. */
-
- static int maybe_positional(format)
- const char *format;
- {
- const char *p;
-
- p = format;
- for (;;) {
- p = strchr(p, '$');
- if (!p)
- return 0;
- if (p - format >= 2
- && isdigit((unsigned char)p[-1])
- && (p[-2] == '%' || p[-2] == '*'))
- break; /* might be a positional thingy */
- }
- return 1;
- }
-
- static int xdoprt(handle, func, format, ap)
- UNIV handle;
- printer func;
- const char *format;
- va_list ap;
- {
- enum arg_type arg_type[9];
- union arg arg[9];
- int nargs, i;
-
- if (!find_arg_types(format, arg_type))
- return -1;
-
- for (nargs = 0; nargs < 9; nargs++)
- if (arg_type[nargs] == NONE)
- break;
-
- for (i = nargs; i < 9; i++)
- if (arg_type[i] != NONE)
- return -1;
-
- for (i = 0; i < nargs; i++)
- get_arg(arg_type[i], &ap, arg + i);
-
- return printit(handle, func, format, ap, nargs, arg);
- }
-
- #ifdef VARARGS
- static int do_fprintf(va_alist) va_dcl
- #else
- static int do_fprintf(UNIV p, const char *format,...)
- #endif
- {
- #ifdef VARARGS
- UNIV p;
- const char *format;
- #endif
- va_list ap;
- int res;
-
- #ifdef VARARGS
- va_start(ap);
- p = va_arg(ap, UNIV);
- format = va_arg(ap, char *);
- #else
- va_start(ap, format);
- #endif
-
- res = vfprintf((FILE *)p, format, ap);
- va_end(ap);
- return res;
- }
-
- #ifdef VARARGS
- int xfprintf(va_alist) va_dcl
- #else
- int xfprintf(FILE *fp, const char *format, ...)
- #endif
- {
- #ifdef VARARGS
- FILE *fp;
- char *format;
- #endif
- va_list ap;
- int res;
-
- #ifdef VARARGS
- va_start(ap);
- fp = va_arg(ap, FILE *);
- format = va_arg(ap, char *);
- #else
- va_start(ap, format);
- #endif
- if (maybe_positional(format))
- res = xdoprt((UNIV)fp, do_fprintf, format, ap);
- else
- res = vfprintf(fp, format, ap);
- va_end(ap);
- return res;
- }
-
- int xvfprintf(fp, format, ap)
- FILE *fp;
- const char *format;
- va_list ap;
- {
- int res;
- if (maybe_positional(format))
- res = xdoprt((UNIV)fp, do_fprintf, format, ap);
- else
- res = vfprintf(fp, format, ap);
- return res;
- }
-
- #endif /* not HAVE_EXTENDED_PRINTF */
-